﻿using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DirectXTest.Src
{
    public static class ScaleSurfaceTest
    {
        public unsafe static void Start()
        {
            string Name = "007";
            NativeMethods.highgui_namedWindow(Name, 0);
            NativeMethods.highgui_resizeWindow(Name, 400, 400);
            Mat mat = new Mat(@"D:\VideoFolder\001.png").Resize(new Size(400, 400));
            Mat target = new Mat(mat.Size(), mat.Type());

            Mat mapx = new Mat(mat.Size(), MatType.CV_32FC1);
            Mat mapy = new Mat(mat.Size(), MatType.CV_32FC1);

            for (int i = 0; i < mat.Height; i++)
            {
                unsafe
                {
                    for (int j = 0; j < mat.Width; j++)
                    {

                        IntPtr item = mapx.Ptr(i, j);
                        float* data = (float*)item.ToPointer();
                        data[0] = j;

                        item = mapy.Ptr(i, j);
                        data = (float*)item.ToPointer();
                        data[0] = i;
                    }
                }
            }

            List<Rect> rects = new List<Rect>();
            for (int i = 0; i < 10; i++)
            {
                for (int j = 0; j < 10; j++)
                {
                    Rect rect = new Rect(i * 40, j * 40, 40, 40);
                    rects.Add(rect);
                    Cv2.Rectangle(mat, rect, Scalar.Red);
                }
            }

            int distance = 100;

            Point originPoint = new Point(0, 0);
            Point targetPoint = new Point(15, 15);
            double stepx = Math.PI / (mat.Width * 1.0);
            double stepy = Math.PI / (mat.Height * 1.0);
            for (int i = 0; i < mat.Height ; i++)
            {
                unsafe
                {
                    for (int j = 0; j < mat.Width; j++)
                    {
                        double x = j - distance * ((Math.Cos(j * stepx) +1));
                        IntPtr itemx = mapx.Ptr(i, j);
                        float* datax = (float*)itemx.ToPointer();
                        datax[0] = Convert.ToSingle(x);

                        /* double y = i - distance * (Math.Cos(i * stepx) + 1);
                         IntPtr itemy = mapy.Ptr(i, j);
                         float* datay = (float*)itemy.ToPointer();
                         datay[0] = Convert.ToSingle(y);*/

                    }
                }
            }

            DateTime dateTime1 = DateTime.Now;
            Cv2.Remap(mat, target, mapx, mapy, InterpolationFlags.Linear, BorderTypes.Constant, new Scalar(0, 0, 0));
            DateTime dateTime2 = DateTime.Now;
            System.Console.WriteLine(dateTime2.Millisecond - dateTime1.Millisecond);
            NativeMethods.highgui_imshow(Name, target.CvPtr);
            Cv2.WaitKey();
        }

        /// <summary>
        /// 移动最小二乘法的具体计算过程，参照论文“基于移动最小二乘法的曲线曲面拟合”，AB矩阵参照论文“移动最小二乘法的研究”
        /// </summary>
        /// <returns></returns>
        private static Point CalcuteMLS(List<Point> originPoints, List<Point> targetPoints, Point input)
        {
            List<double> weights = new List<double>();
            //计算当前点到原始控制点的权重
            for (int i = 0; i < originPoints.Count; i++)
            {
                Point item = originPoints[i];
                double weight = double.MaxValue;
                if (item.X == input.X && item.Y == input.Y)
                {
                }
                else
                {
                    weight = 1 / (Math.Pow(item.X - input.X, 2) * 1.0 + Math.Pow(item.Y - input.Y, 2) * 1.0);
                }
                weights.Add(weight);
            }

            double px = 0, py = 0, qx = 0, qy = 0, tw = 0;
            for (int i = 0; i < weights.Count; i++)
            {
                double weight = weights[i];
                Point originPoint = originPoints[i];
                Point targetPoint = targetPoints[i];
                px += weight * originPoint.X;//所有控制顶点p的加权位置
                py += weight * originPoint.Y;
                qx += weight * targetPoint.X;//所有控制顶点q的加权位置
                qy += weight * targetPoint.Y;
                tw += weight;//总权重
            }
            Point pPoint = new Point(px / tw, py / tw);
            Point qPoint = new Point(qx / tw, qy / tw);

            double[,] A = new double[2, 2] { { 0,0},{ 0, 0 } };
            double[,] B = new double[2, 2] { { 0, 0 }, { 0, 0 } };
            double[,] M = new double[2, 2] { { 0, 0 }, { 0, 0 } };
            for (int p = 0; p < weights.Count; p++)
            {
                double weight = weights[p];
                Point originPoint = originPoints[p];
                Point targetPoint = targetPoints[p];
                double[] P = new double[2] { originPoint.X - pPoint.X, originPoint.Y - pPoint.Y };
                double[,] PT = new double[2, 1];
                PT[0, 0] = originPoint.X - pPoint.X;
                PT[1, 0] = originPoint.Y - pPoint.Y;
                double[] Q = new double[2] { targetPoint.X - qPoint.X, targetPoint.Y - qPoint.Y };
                double[,] T = new double[2, 2];
                T[0, 0] = PT[0, 0] * P[0];
                T[0, 1] = PT[0, 0] * P[1];
                T[1, 0] = PT[1, 0] * P[0];
                T[1, 1] = PT[1, 0] * P[1];
                for (int i = 0; i < 2; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        A[i, j] += weight * T[i, j];
                    }
                }
                T[0, 0] = PT[0, 0] * Q[0];
                T[0, 1] = PT[0, 0] * Q[1];
                T[1, 0] = PT[1, 0] * Q[0];
                T[1, 1] = PT[1, 0] * Q[1];
                for (int i = 0; i < 2; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        B[i, j] += weight * T[i, j];
                    }
                }
            }
            double det = A[0, 0] * A[1, 1] - A[0, 1] * A[1, 0];
            Point fvPoint = new Point();
            if (det < 0.0000001)
            {
                fvPoint.X = input.X + qPoint.X - pPoint.X;
                fvPoint.Y = input.Y + qPoint.Y - pPoint.Y;
                return fvPoint;
            }
            double temp1, temp2, temp3, temp4;
            temp1 = A[1, 1] / det;
            temp2 = -A[0, 1] / det;
            temp3 = -A[1, 0] / det;
            temp4 = A[0, 0] / det;
            A[0, 0] = temp1;
            A[0, 1] = temp2;
            A[1, 0] = temp3;
            A[1, 1] = temp4;
            M[0, 0] = A[0, 0] * B[0, 0] + A[0, 1] * B[1, 0];
            M[0, 1] = A[0, 0] * B[0, 1] + A[0, 1] * B[1, 1];
            M[1, 0] = A[1, 0] * B[0, 0] + A[1, 1] * B[1, 0];
            M[1, 1] = A[1, 0] * B[0, 1] + A[1, 1] * B[1, 1];
            double[] V = new double[2] { input.X - pPoint.X, input.Y - pPoint.Y };
            double[,] R = new double[2, 1];
            R[0, 0] = V[0] * M[0, 0] + V[1] * M[1, 0];//lv（x）总计算公式
            R[1, 0] = V[0] * M[0, 1] + V[1] * M[1, 1];
            if ( double.IsNaN(R[0, 0]))
            {
                return input;
            }
            fvPoint.X = Convert.ToInt32(R[0, 0] + qPoint.X);
            fvPoint.Y = Convert.ToInt32(R[1, 0] + qPoint.Y);
            return fvPoint;
        }
    }
}